var buster = require("buster");
var assert = buster.assert;
var all = require("node-promise/promise").all;
var todoList = require("../lib/todo").todoList;

buster.testCase("Todo list", {
    "fetching all todos": {
        "should get empty array when no items": function (done) {
            var list = todoList.create();

            list.all(function (items) {
                assert.equals(items.length, 0);
                done();
            });
        },

        "should return promise that resolves with items": function (done) {
            var list = todoList.create();
            var promise = list.all();

            assert.equals(typeof promise, "object");
            assert.equals(typeof promise.then, "function");

            promise.then(function (items) {
                assert.equals(items.length, 0);
                done();
            });
        },

        "should not return promise when passing callback": function () {
            var list = todoList.create();
            var promise = list.all(this.spy());

            assert.isUndefined(promise);
        },

        "should get added todo item": function (done) {
            var list = Object.create(todoList);

            list.add("Just do it!", function (item) {
                list.all(function (items) {
                    assert.equals(items, [item]);
                    done();
                });
            });
        }
    },

    "adding todos": {
        "should yield new item to callback": function (done) {
            var list = Object.create(todoList);

            list.add("Just do it!", function (item) {
                assert.equals(item.text, "Just do it!");
                assert.isNumber(item.id);
                assert(!item.done);
                done();
            });
        },

        "should return promise resolving with new item": function (done) {
            var list = Object.create(todoList);

            list.add("Just do it!").then(function (item) {
                assert.equals(item.text, "Just do it!");
                assert.isNumber(item.id);
                assert.isFalse(item.done);
                done();
            });
        },

        "should not return promise when passing callback": function () {
            var list = Object.create(todoList);
            var promise = list.add("Just do it!", this.spy());

            assert.isUndefined(promise);
        },

        "should add several items": function (done) {
            var list = Object.create(todoList);

            all(list.add("Go!"),
                list.add("Run"),
                list.add("Sit")).then(function () {
                list.all(function (items) {
                    assert.equals(items.length, 3);
                    done();
                });
            });
        }
    },

    "completing todos": {
        "should mark item with id as done": function (done) {
            var list = Object.create(todoList);

            list.add("Run for the hills", function (item) {
                list.complete(item.id, function (i) {
                    assert(item.done);
                    assert.equals(i, item);
                    done();
                });
            });
        },

        "should return promise": function (done) {
            var list = Object.create(todoList);

            list.add("Run for the hills").then(function (item) {
                list.complete(item.id).then(function (i) {
                    assert(item.done);
                    assert.equals(i, item);
                    done();
                });
            });
        },

        "should not return promise if callback is passed": function (done) {
            var list = Object.create(todoList);

            list.add("Run for the hills", function (item) {
                var promise = list.complete(item.id, this.spy());
                assert.isUndefined(promise);
                done();
            }.bind(this));
        },

        "should pass item as null and error if no such item exists": function (done) {
            var list = Object.create(todoList);

            list.complete(213, function (item, err) {
                assert.isNull(item);
                assert.equals(err.message, "No such item");
                done();
            });
        },

        "should reject promise if no such item exists": function (done) {
            var list = Object.create(todoList);

            list.complete(213).then(this.spy(), function (err) {
                assert.equals(err.message, "No such item");
                done();
            });
        }
    },

    "getting todo": {
        "should get specific item": function (done) {
            var list = Object.create(todoList);

            list.add("Go go go", function (added) {
                list.get(added.id, function (fetched) {
                    assert.equals(fetched, added);
                    done();
                });
            });
        },

        "should err if item does not exist": function (done) {
            var list = Object.create(todoList);

            list.get(799, function (item, err) {
                assert.isNull(item);
                assert.equals(err.message, "No such error");
                done();
            });
        },

        "should return promise": function (done) {
            var list = Object.create(todoList);

            list.add("Go go go", function (added) {
                list.get(added.id).then(function (fetched) {
                    assert.equals(fetched, added);
                    done();
                });
            });
        },

        "should not return promise if callback is passed": function (done) {
            var list = Object.create(todoList);

            list.add("Go go go", function (item) {
                var promise = list.get(item.id, this.spy());

                assert.isUndefined(promise);
                done();
            }.bind(this));
        },

        "should reject promise if item does not exist": function (done) {
            var list = Object.create(todoList);

            list.get(799).then(this.spy(), function (err) {
                assert.equals(err.message, "No such error");
                done();
            });
        }
    }
});
